package cn.tk.engine.mr;

import cn.tk.cache.mr.FlowBuf;
import cn.tk.model.entity.mr.Flow;
import cn.tk.model.pojo.mr.engine.ClarifyItem;
import cn.tk.model.pojo.mr.engine.ClarifyResult;
import cn.tk.model.pojo.mr.engine.MrSession;
import cn.tk.model.pojo.mr.flow.ConditionDTO;
import cn.tk.model.pojo.mr.flow.ConditionNodeDTO;
import cn.tk.model.pojo.mr.nlu.NluResp;
import cn.tk.service.mr.ConditionNodeService;
import cn.tk.service.mr.SessionService;
import cn.tk.utils.EmptyUtil;
import cn.tk.utils.JsEngine;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.script.Bindings;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;

/**
 * Created by denglw on 2021/7/29.<br/>
 * Desc: 处理条件节点逻辑
 */
@Slf4j
@Component
public class ConditionNodeDeal {

    @Autowired
    private ConditionNodeService conditionNodeService;

    @Autowired
    private SessionService sessionService;

    /**
     * 匹配子条件节点.<br/>
     * @param nlu 模型结果
     * @param flowId 流程id
     * @param lastNodeId 上一轮次节点id
     * @return node
     */
    public ConditionNodeDTO searchChild(NluResp nlu, Long flowId, Long lastNodeId) {
        if (EmptyUtil.anyNull(flowId, lastNodeId) || nlu == null) {
            return null;
        }
        FlowBuf flowBuf = FlowBuf.getInstance();
        // 1、子条件节点跳转
        // 跳转规则：如果unionRule=3，直接跳入；如果unionRule=4，优先匹配兄弟节点，该节点进行兜底
        // 跳转细则：如果unionRule=1，需要所有子条件都匹配才跳入；如果unionRule=2，则满意其中一个条件就跳入
        List<Long> childNodeIds = flowBuf.childConditionNodeIds(lastNodeId);
        log.info("节点[{}]的子条件节点集合: [{}].", lastNodeId, childNodeIds);
        List<ConditionNodeDTO> childNodes = conditionNodeService.conditionNodes(childNodeIds);
        return this.searchConditionNode(nlu, childNodes);
    }

    /**
     * 匹配可跳转条件节点.<br/>
     * @param nlu 模型结果
     * @param flowId 流程id
     * @param lastNodeId 上一轮次末尾节点id
     * @return 匹配的条件节点
     */
    public ConditionNodeDTO searchRecover(NluResp nlu, Long flowId, Long lastNodeId) {
        if (EmptyUtil.anyNull(flowId, lastNodeId) || nlu == null) {
            return null;
        }
        FlowBuf flowBuf = FlowBuf.getInstance();
        // 寻找可恢复跳转的条件节点进行跳转匹配
        // 跳转规则：如果unionRule=3，直接跳入；如果unionRule=4，优先匹配兄弟节点，该节点进行兜底
        // 跳转细则：如果unionRule=1，需要所有子条件都匹配才跳入；如果unionRule=2，则满意其中一个条件就跳入
        Flow flow = flowBuf.getFlow(flowId);
        if (flow == null) {
            return null;
        }
        List<ConditionNodeDTO> recoverableNodes = conditionNodeService.recoverableConditionNodes(flow.getBotId());
        // 相同流程的节点优先
        recoverableNodes.sort((a, b) -> {
            if (Objects.equals(a.getFlowId(), flowId)) {
                return 1;
            }
            if (Objects.equals(b.getFlowId(), flowId)) {
                return -1;
            }
            return 0;
        });
        return this.searchConditionNode(nlu, recoverableNodes);
    }

    private ConditionNodeDTO searchConditionNode(NluResp nlu, List<ConditionNodeDTO> conditionNodes) {
        ConditionNodeDTO bottomNode = null;
        for (ConditionNodeDTO node : conditionNodes) {
            if (node.isNotNullRule()) {
                return node;
            }
            if (node.isExcludeAllRule()) {
                bottomNode = node;
            }
            // 子条件匹配
            List<ConditionDTO> conditions = node.getConditions();
            // 匹配成功次数
            int successNum = 0;
            for (ConditionDTO condition : conditions) {
                boolean match = this.conditionMatch(nlu, condition);
                if (match) {
                    successNum ++;
                }
            }
            if (node.isAllRule()) {
                if (successNum == conditions.size()) {
                    return node;
                }
            }
            if (node.isAnyRule()) {
                if (successNum > 0) {
                    return node;
                }
            }
        }
        return bottomNode;
    }

    private boolean conditionMatch(NluResp nlu, ConditionDTO condition) {
        NluResp.InnerIntent intent = nlu.getSecondaryIntent();
        List<NluResp.InnerEntity> entities = this.packInnerEntities(nlu);
        if (condition.isIntentType()) {
            String conditionIntentCode = condition.getIntentCode();
            if (intent == null || EmptyUtil.blank(conditionIntentCode)) {
                return false;
            }
            return Objects.equals(intent.getIntent(), conditionIntentCode);
        }

        if (condition.isEntityType()) {
            if (EmptyUtil.emptyColl(entities)) {
                return false;
            }
            String conditionEntityCode = condition.getEntityCode();
            String symbol = condition.entityValueRule2Symbol();
            // 如果没有比较运算符，默认返回失败
            if (EmptyUtil.blank(symbol)) {
                return false;
            }
            String conditionEntityValue = condition.getEntityValue();
            for (NluResp.InnerEntity entity : entities) {
                String entityCode = entity.getEntityCode();
                if (!Objects.equals(entityCode, conditionEntityCode)) {
                    continue;
                }
                String entityValue = entity.getValue();
                if (EmptyUtil.blank(entityValue)) {
                    continue;
                }
                // 表达式组装，并进行js运算
                JsEngine jsEngine = JsEngine.of();
                Bindings bindings = jsEngine.createBindings();
                bindings.put("entityValue", entityValue);
                bindings.put("conditionEntityValue", conditionEntityValue);
                String expression = "entityValue" + symbol + "conditionEntityValue";
                boolean evalBool = jsEngine.evalBool(expression, bindings);
                if (evalBool) {
                    return true;
                }
            }
        }
        return false;
    }

    /**
     * 需要将当前轮次已经澄清的实体加入.<br/>
     * @param nlu 模型结果
     * @return 实体集
     */
    private List<NluResp.InnerEntity> packInnerEntities(NluResp nlu) {
        List<NluResp.InnerEntity> entities = new ArrayList<>(EmptyUtil.nullToEmptyList(nlu.getEntities()));
        // 需要将当前轮次的澄清成功的结果设置进去
        MrSession.Round currRound = sessionService.fetchCurrRound();
        ClarifyResult clarifyResult = currRound.getClarifyResult();
        if (clarifyResult == null) {
            return entities;
        }
        List<ClarifyItem> clarifyItems = EmptyUtil.nullToEmptyList(clarifyResult.getClarifyItems());
        for (ClarifyItem item : clarifyItems) {
            if (!item.isClarified() || EmptyUtil.emptyColl(item.getClarifyValues())) {
                continue;
            }
            String entityCode = item.getEntityCode();
            String entityValue = item.getClarifyValues().get(0).getContent();
            boolean contains = entities.stream().anyMatch(i -> Objects.equals(i.getEntityCode(), entityCode));
            if (!contains) {
                entities.add(new NluResp.InnerEntity(entityCode, entityValue));
            }
        }
        return entities;
    }

}
